﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Geometry;

namespace RTS
{
	public class PosicionableObject
	{
		public const String Death = "Death";

		private const float animationFrameDuration = 0.1F;
		private const float timeShowingLifeBar = 2F;
		private const float timeWaitTillDissapear = 3F;
		private const float timeOfDeath = 1.25F;

		// Orientations
		public const int Down = 0;
		public const int RightDown = 1;
		public const int Right = 2;
		public const int RightUp = 3;
		public const int Up = 4;
		public const int LeftUp = 5;
		public const int Left = 6;
		public const int LeftDown = 7;

		protected Camera camera;
		protected RTSTextures textures;
		protected Position2D pos;
		protected GameMain game;
		protected bool isSelected = false;
		private bool contentLoaded = false;

		protected int attackDamage = 15;
		protected float delayBetweenAttacks = 1.25F;
		protected float delayBetweenAttacksCounter = 0;
		protected Position2D graphPositionOffset = new Position2D(0, 0);
		protected float collisionRadius;
		protected int maxLife;
		protected int life;
		protected PosicionableObject attackTo = null;
		protected Position2D attackToPrevPos = null;
		protected float timeSinceAttack = float.PositiveInfinity;

		protected bool drawByPercentage = false;
		protected float stateTimeElapsed = 0;
		protected int orientation;
		protected String state = "idle";

		public float CollisionRadius
		{
			get { return collisionRadius; }
		}

		public PosicionableObject(GameMain game)
		{
			this.game = game;
		}

		public GeomVector2 getCollisionCircleCenter()
		{
			return new GeomVector2(pos.X, pos.Y);
		}

		public void assignCamera(Camera camera)
		{
			this.camera = camera;
		}

		public virtual void setPosition(Position2D pos)
		{
			this.pos = pos;
		}

		public Position2D getPosition()
		{
			return pos;
		}

		protected void move(float x, float y)
		{
			pos.add(x, y);
		}

		public bool IsSelected()
		{
			return isSelected;
		}

		public void select()
		{
			isSelected = true;
		}

		public void unselect()
		{
			isSelected = false;
		}

		public int getLife()
		{
			return life;
		}

		public float getPercentLife()
		{ 
			return (float)life / (float)maxLife;
		}

		public virtual float getTimeElapsed()
		{
			return stateTimeElapsed;
		}

		public int getOrientation()
		{
			return orientation;
		}

		public void resetTimeSinceAttackCounter()
		{
			timeSinceAttack = 0;
		}

		public void Damage(int amountDamage)
		{
			life -= amountDamage;

			if (life <= 0)
			{
				setState(Death);
				game.unselectObject(this);
			}
		}

		public virtual void update()
		{
			const float timeElapsed = 0.02F;

			delayBetweenAttacksCounter += timeElapsed;
			timeSinceAttack += timeElapsed;
			stateTimeElapsed += timeElapsed;
		}

		public virtual bool isClicked(int mouseX, int mouseY)
		{
			if (state == Death) return false;

			int textureWidth = textures.TextureSize.Width;
			int textureHeight = textures.TextureSize.Height;

			Vector2 drawingVector = getDrawingVector();
			Rectangle rectanglePosition = new Rectangle((int)drawingVector.X, (int)drawingVector.Y, (int)(textureWidth * camera.Scale), (int)(textureHeight * camera.Scale));

			Texture2D selectionArea = textures.GetSelectionAreaTexture();

			if (rectanglePosition.Contains(new Point(mouseX, mouseY)))
			{
				return (getAlphaInTexturePixel(mouseX - (int)drawingVector.X, mouseY - (int)drawingVector.Y, selectionArea) != 0);
			}

			return false;
		}

		protected int getAlphaInTexturePixel(int x, int y, Texture2D texture)
		{
			int xScaled = (int)(x / camera.Scale);
			int yScaled = (int)(y / camera.Scale);

			Color[] color = new Color[texture.Width * texture.Height];
			texture.GetData(color);

			return color[xScaled + yScaled * texture.Width].A;
		}

		public virtual void Draw(SpriteBatch spriteBatch)
		{
			TextureSize textureSize = textures.TextureSize;

			if (isSelected) spriteBatch.Draw(textures.GetSelectedTexture(), getDrawingVector(), null, Color.White, 0, new Vector2(0, 0), camera.Scale, SpriteEffects.None, 0);
			drawInMiddle(spriteBatch);
			if (isSelected || timeSinceAttack < timeShowingLifeBar && state != Death) drawLifeBar(spriteBatch, camera);
		}

		protected virtual void drawInMiddle(SpriteBatch spriteBatch)
		{ 
		
		}

		private void drawLifeBar(SpriteBatch spriteBatch, Camera camera)
		{
			float lifePercent = getPercentLife();
			
			TextureSize textureSize = textures.TextureSize;

			spriteBatch.Draw(textures.GetLifeBarBackgroundTexture(), getDrawingVector(), null, Color.White, 0, new Vector2(0, 0), camera.Scale, SpriteEffects.None, 0);

			Texture2D lifeBarTexture = textures.GetLifeBarTexture();
			spriteBatch.Draw(lifeBarTexture, getDrawingVector(), new Rectangle(0, 0, (int)(textures.LifeBarOffsetX + (textures.LifeBarWidth) * lifePercent), lifeBarTexture.Height), Color.White, 0, new Vector2(0, 0), camera.Scale, SpriteEffects.None, 0);

		}


		public virtual void LoadContent(ContentManager Content)
		{
			contentLoaded = true;
		}

		public virtual void excecuteClickAction(List<PosicionableObject> clickedObjects, MouseState mouse)
		{
			if (state == Death) return;
		}

		protected void drawObject(SpriteBatch spriteBatch, List<Texture2D> currentTextures, TextureSize textureSize)
		{
			// Count the amount of total frames in the animation, looping all the texture parts.
			int frames = 0;
			for (int i = 0; i < currentTextures.Count; i++)
			{
				frames += (int)(currentTextures[i].Width / textureSize.Width);
			}
			
			int xPos;
			if (drawByPercentage)
			{
				xPos = (int)(textureSize.Width * Math.Floor(frames * getDrawingPercentage()));
			} else {
				xPos = (int)((getTimeElapsed() % (frames * animationFrameDuration)) / animationFrameDuration) * textureSize.Width;
			}

			// Check in which part the current texture to show is.
			int texturePart = 0;
			for (int i = 0; i < currentTextures.Count; i++)
			{
				int textureWidth = currentTextures[i].Width;

				if (textureWidth <= xPos)
				{
					xPos = xPos - textureWidth;
				}
				else
				{
					texturePart = i;
					break;
				}
			}

			Rectangle rectangle = new Rectangle(xPos, textureSize.Height * getOrientation(), textureSize.Width, textureSize.Height);

			Texture2D currentTexture = currentTextures[texturePart];

			spriteBatch.Draw(currentTexture, getDrawingVector(), rectangle, Color.White, 0, new Vector2(0, 0), camera.Scale, SpriteEffects.None, 0);
		}

		protected Vector2 getDrawingVector()
		{
			TextureSize textureSize = textures.TextureSize;

			float XDrawingPos = pos.X - textureSize.Width * camera.Scale / 2 - camera.CornerPos.X + graphPositionOffset.X;
			float YDrawingPos = pos.Y - textureSize.Height * camera.Scale / 2 - camera.CornerPos.Y + graphPositionOffset.Y;

			return new Vector2(XDrawingPos, YDrawingPos);
		}

		protected virtual void setState(String newState)
		{
			drawByPercentage = false;

			if (state != newState)
			{
				state = newState;
				stateTimeElapsed = 0;
			}

			if (state == Death) drawByPercentage = true;
		}

		protected virtual float getDrawingPercentage()
		{
			if (state == Death)
			{
				if (stateTimeElapsed >= timeOfDeath) return (1F - (float)Math.Pow(10, -5));
				return stateTimeElapsed / timeOfDeath;
			}
			return 1F;
		}

		public bool isDead()
		{
			return (life <= 0);
		}
	}
}
